package de.tum.wi.som.prio.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import de.tum.wi.som.Node;
import de.tum.wi.som.Project;
import de.tum.wi.som.prio.PriorityRule;
import de.tum.wi.som.util.PrioUtil;

/**
 * Weighted activity criticality resource utilization
 * 
 * Max(w sum_N_i(1+SLK_iq)^(-alpha)+(1-w)sum_K(r_ik/R_max,k))
 * 
 * <pre>
 * w = 0.5; weight associated with N_i (0<=w<=1)
 * alpha = 0.5; weight parameter
 * N_i = the number of immediate successors of the ith activity
 * SLK_iq = slack in the qth immediate successor of the ith activity
 * K = number of types of resources used by project l
 * r_ik = amount of resource type k required by activity i in project l
 * R_max,k = renewable amount of resource type k available in each time interval
 * </pre>
 * 
 * @author Goetz Epperlein
 */
public class WeightedActivityCriticalityResourceUtilization implements
		PriorityRule {

	public static final Double W = 0.5;
	public static final Double ALPHA = 0.5;

	@Override
	public void prioritize(Project project) {

		Node source = project.getSource();
		List<Node> accessibleNodes;
		List<Node> remainingNodes = new ArrayList<Node>();
		List<Node> scheduledNodes = new ArrayList<Node>();
		Node dummyNode = new Node(0, new int[0]);
		Node candidateNode = dummyNode;
		Double maxWACRU = Double.MIN_VALUE;
		Double currentWACRU = Double.MIN_VALUE;

		// calc latest finish time
		project.calcProjectGraph();
		remainingNodes.addAll(Arrays.asList(project.getNodes()));

		while (!remainingNodes.isEmpty()) {
			maxWACRU = Double.MIN_VALUE;
			candidateNode = dummyNode;
			accessibleNodes = PrioUtil.determineAccesibleNodes(scheduledNodes,
					source, remainingNodes);
			if (accessibleNodes.size() == 0) {
				System.err
						.println("No accessible nodes found in WeightedActivityCriticalityResourceUtilization");
				return;
			}

			Iterator<Node> it = accessibleNodes.iterator();

			while (it.hasNext()) {
				Node currentNode = it.next();

				// calc
				currentWACRU = W * calcSLKOfImmediateSuccessors(currentNode)
						+ (1 - W) * calcRessources(currentNode, project);

				if (currentWACRU > maxWACRU) {
					maxWACRU = currentWACRU;
					candidateNode = currentNode;
				}
			}
			if (candidateNode != dummyNode) {
				scheduledNodes.add(candidateNode);
				remainingNodes.remove(candidateNode);
			} else {
				System.err.println("Could not find next node for WeightedActivityCriticalityResourceUtilization");
			}
		}

		project.setNodes(scheduledNodes.toArray(new Node[scheduledNodes.size()]));

	}

	protected Double calcSLKOfImmediateSuccessors(Node node) {

		Double slack = new Double(0);
		List<Node> successors = node.getSuccessors();

		for (Node s : successors) {
			slack += Math.pow((1 + s.getSlack()), -ALPHA);
		}

		return slack;
	}

	protected Double calcRessources(Node node, Project project) {

		int[] ressourcesActivity = node.getR();
		int[] ressourcesProject = project.getR();
		Double ressourceMeasure = new Double(0);

		for (int i = 0; i < ressourcesActivity.length; i++) {
			ressourceMeasure += ressourcesActivity[i] / ressourcesProject[i];
		}

		return ressourceMeasure;
	}

	@Override
	public String toString() {
		return "WeightedActivityCriticalityResourceUtilization";
	}
}
